#include "../esmd_types.h"
#include "simd_cpp.hpp"
template <typename T>
struct unrolled_var {
  T v0, v1, v2, v3;
  unrolled_var() {}
  unrolled_var(T v0, T v1, T v2, T v3) : v0(v0), v1(v1), v2(v2), v3(v3) {}
};

#define DEF_U2U_OP(op)                                                                                   \
  template <typename T0, typename T1>                                                                    \
  __always_inline unrolled_var<OpType<T0, T1>> operator op(unrolled_var<T0> o0, unrolled_var<T1> o1) {   \
    return unrolled_var<OpType<T0, T1>>(o0.v0 op o1.v0, o0.v1 op o1.v1, o0.v2 op o1.v2, o0.v3 op o1.v3); \
  }
#define DEF_U2T_OP(op)                                                                       \
  template <typename T0, typename T1>                                                        \
  __always_inline unrolled_var<OpType<T0, T1>> operator op(unrolled_var<T0> o0, T1 o1) {     \
    return unrolled_var<OpType<T0, T1>>(o0.v0 op o1, o0.v1 op o1, o0.v2 op o1, o0.v3 op o1); \
  }
#define DEF_T2U_OP(op)                                                                       \
  template <typename T0, typename T1>                                                        \
  __always_inline unrolled_var<OpType<T0, T1>> operator op(T0 o0, unrolled_var<T1> o1) {     \
    return unrolled_var<OpType<T0, T1>>(o0 op o1.v0, o0 op o1.v1, o0 op o1.v2, o0 op o1.v3); \
  }

DEF_U2U_OP(+)
DEF_U2U_OP(-)
DEF_U2U_OP(*)
DEF_U2U_OP(/)
DEF_U2T_OP(+)
DEF_U2T_OP(-)
DEF_U2T_OP(*)
DEF_U2T_OP(/)
DEF_T2U_OP(+)
DEF_T2U_OP(-)
DEF_T2U_OP(*)
DEF_T2U_OP(/)

template <typename T0>
__always_inline unrolled_var<T0> operator-(unrolled_var<T0> o0) {
  return unrolled_var<T0>(-o0.v0, -o0.v1, -o0.v2, -o0.v3);
}
// #include <type_traits>
// #define DEF_UNARY_FUNC(func)                                                   \
//   template <typename T0>                                                       \
//   __always_inline unrolled_var<typename std::result_of<func(T0)>::type> func(unrolled_var<T0> o0) { \
//     return unrolled_var(func(o0.v0), func(o0.v1), func(o0.v2), func(o0.v3));   \
//   }
// DEF_UNARY_FUNC(v_set1d);